﻿using System;
using System.Collections.Generic;
using mshtml;

namespace MQuery
{
    /// <summary>
    /// The main Query class
    /// </summary>
    public class Query : IEnumerable<IHTMLElement>
    {
        /// <summary>
        /// Q 
        /// $(http://docs.jquery.com/Core/%24)
        /// </summary>
        /// <param name="query">The query string.</param>
        /// <param name="document">The document to query.</param>
        /// <returns>The query's result</returns>
        public static Query Q(string query, HTMLDocumentClass document)
        {
            return new Query(query, document);
        }

        /// <summary>
        /// Q 
        /// $(http://docs.jquery.com/Core/%24)
        /// </summary>
        /// <param name="query">The query string.</param>
        /// <param name="element">The element to query.</param>
        /// <returns>The query's result</returns>
        public static Query Q(string query, IHTMLElement element)
        {
            return new Query(query, element);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Query"/> class.
        /// </summary>
        /// <param name="query">The query string.</param>
        /// <param name="document">The document to query.</param>
        public Query(string query, HTMLDocumentClass document)
        {
            foreach (string queryPart in query.Split(' '))
            {
                string[] brokenQueryPart = queryPart.Split(':');
                
                
                string selectorPart = brokenQueryPart[0];
                ISelector select = GetSelector(selectorPart);
                if (elements == null)
                {
                    elements = select.Select(document);
                }
                else
                {
                    elements = select.Select(elements);
                }

                if (elements.Count == 0)
                {
                    break;
                }

                if (brokenQueryPart.Length == 2)
                {
                    Filters.Filter(brokenQueryPart[1], elements);
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Query"/> class.
        /// </summary>
        /// <param name="query">The query string.</param>
        /// <param name="element">The element to query.</param>
        public Query(string query, IHTMLElement element)
        {
            foreach (string queryPart in query.Split(' '))
            {
                string[] brokenQueryPart= query.Split(':');
                string selectorPart = brokenQueryPart[0];

                ISelector select = GetSelector(selectorPart);
                if (elements == null)
                {
                    elements = select.Select(element);
                }
                else
                {
                    elements = select.Select(elements);
                }

                if (elements.Count == 0)
                {
                    break;
                }

                if (brokenQueryPart.Length == 2)
                {
                    Filters.Filter(brokenQueryPart[1], elements);
                }
            }
        }

        /// <summary>
        /// Retrieves an attribute of the first matched element
        /// $(http://docs.jquery.com/Attributes/attr)
        /// </summary>
        /// <param name="name">The attribute's name.</param>
        /// <returns>The attribute's value if exists or null if no item <paramref name="Lenght"/>==0 or attribute does not exist</returns>
        public string Attribute(string name)
        {
            if (Length == 0)
            {
                return null;
            }
            return this[0].Attribute(name);
        }

        /// <summary>
        /// Get the text contents of the first matched element. 
        /// $(http://docs.jquery.com/Attributes/text)
        /// </summary>
        /// <returns>The text contents of the first matched element.</returns>
        //TODO: Should return a combined text of all matched elements
        public string Text()
        {
            if (Length == 0)
            {
                return null;
            }
            return this[0].Text();
        }

        /// <summary>
        /// Iterate over the selected elements and performs the callback on each of them.
        /// $(http://docs.jquery.com/Utilities/jQuery.each)
        /// </summary>
        /// <param name="callback">(index,element) callback. The index is 0 based</param>
        public void Each( Action<int,IHTMLElement> callback )
        {
            for (int index = 0; index < elements.Count; ++index)
            {
                callback(index, elements[index]);
            }
        }

        /// <summary>
        /// Gets the number of elements selected by the <see cref="Query"/>
        /// $(http://docs.jquery.com/Core/length)
        /// </summary>
        /// <value>The length.</value>
        public int Length
        {
            get { return elements.Count; }
        }

        public IHTMLElement this[int index]
        {
            get { return elements[index]; }
        }

        #region IEnumerable<object> Members

        public IEnumerator<IHTMLElement> GetEnumerator()
        {
            return elements.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return elements.GetEnumerator();
        }

        #endregion

        private ISelector GetSelector(string selector)
        {
            switch (selector[0])
            {
                case '#':
                    {//ID
                        return new IDSelector(selector.Substring(1));
                    }
                case '.':
                    {//Class
                        return new ClassSelector(selector.Substring(1));
                    }
                default:
                    {//Tag
                        return new ElementSelector(selector);
                    }
            }
        }

        private IList<IHTMLElement> elements;
    }
}
